<?php
/**
 * Ly - 数据库核心类
 *
 * 提供数据库核心功能，其他所有系统需要使用的数据库驱动均需继承该类
 *
 * Copyright (c) 2013 YinHailin All rights reserved.
 * @Author: YinHailin
 * @Authorize: Boren Network
 * @Created by YinHailin at 2013-11-01
 */

defined('LY_VERSION') or exit();

class DbCore {
  /* 数据库类型 */
  protected $DbType = null;
  /* 当前SQL指令 */
  protected $QueryStr = '';
  /* 最后操作ID */
  protected $LastInsertId = null;
  /* 影响记录数 */
  protected  $NumRows = 0;
  /* 错误信息 */
  protected $Error = '';
  /* 是否已连接数据库 */
  protected $Connected = false;
  /* 数据库连接ID，支持数据库多连接 */
  protected $LinkIdList = array();
  /* 当前数据连接ID */
  protected $CurLinkId = null;
  /* 当前数据库查询ID */
  protected $CurQueryId = null;
  /* 数据库连接参数 */
  protected $Config = '';

  /*
   * 获得数据库类实例
   */
  static public function GetInstance() {
    $args = func_get_args();
    return get_instance_of(__CLASS__,'Factory',$args);
  }

  /*
   * 获得数据库类实例
   * @param mixed $dbConfig 数据库配置信息
   * @return object
   */
  public function Factory($dbConfig = '') {
    //解析数据库配置信息
    $dbConfig = $this->ParseConfig($dbConfig);
    //获取数据库类型
    if (empty($dbConfig['type'])) {
      //如果为指定数据库类型
      halt('数据库类型未指定，数据库连接失败', __FILE__, __LINE__);
    }
    $this->DbType = parse_name(strtolower($dbConfig['type']), 1);
    //获取数据库驱动类名并实例化
    $class = $this->DbType.'Db';
    if (!class_exists($class)) {
      halt('数据库驱动类：'.$class.' 不存在', __FILE__, __LINE__);
    } else {
      $db = new $class($dbConfig);
    }
    return $db;
  }

  /*
   * 解析数据库配置
   * @param mixed $dbConfig 数据库配置信息
   * @return array
   */
  private function ParseConfig($dbConfig = '') {
    $array = array();
    if (is_array($dbConfig)) {
      //如果数据库配置信息为数组
      $dbConfig = array_change_key_case($dbConfig, CASE_LOWER);
      $array = array(
        'type' => $dbConfig['db_type'],
        'host' => $dbConfig['db_hose'],
        'port' => $dbConfig['db_port'],
        'user' => $dbConfig['db_user'],
        'password' => $dbConfig['db_password'],
        'database' => $dbConfig['db_name'],
      );
      $dbConfig = $array;
    } else {
      //如果数据库配置信息为空
      $dbConfig = array(
        'type' => C('DB_TYPE'),
        'host' => C('DB_HOST'),
        'port' => C('DB_PORT'),
        'user' => C('DB_USER'),
        'password' => C('DB_PASSWORD'),
        'database' => C('DB_NAME'),
      );
    }
    return $dbConfig;
  }

  /*
   * 初始化数据库连接
   + 该方法用于数据库驱动调用，本身并不调用
   */
  protected function InitConnect() {
    if (!$this->Connected) {
      $this->CurLinkId = $this->Connect();
    }
  }

  /*
   * 字段名解析
   + 预置字段名解析方法
   * @param string $key 字段名
   * @return string
   */
  public function ParseKey($key) {
    return $key;
  }

  /*
   * 字段值解析
   * @param mixed $value 字段值
   * @return string
   */
  public function ParseValue($value) {
    if (is_string($value)) {
      //如果是字符串
      $value = '\''.$this->FilterString($value).'\'';
    } elseif (isset($value[0]) && is_string($value[0]) && strtolower($value[0]) === 'exp') {
      //如果是exp表达式模式
      $value = $this->FilterString($value[1]);
    } elseif (is_array($value)) {
      $value = array_map(array($this, 'FilterString'), $value);
    } elseif (is_bool($value)) {
      $value = $value ? '1' : '0';
    } elseif (is_null($value)) {
      $value = 'null';
    }
    return $value;
  }

  /*
   * SQL指令字符串安全过滤
   * @param string $string SQL指令字符串
   * @return string
   */
  public function FilterString($string) {
    $string = addslashes($string);
    return $string;
  }

  /*
   * Set解析
   * @param array $data 数据
   * @return string
   */
  public function ParseSet($data) {
    $set = array();
    foreach ($data as $key => $value) {
      $value = $this->ParseValue($value);
      if (is_scalar($value)) {
        $set[] = $this->ParseKey($key).'='.$value;
      }
    }
    return ' SET '.implode(',', $set);
  }

  /*
   * Field解析
   + 支持数组方式传递字段名
   + 方式1 字符串方式
   + 方式2 array(字段1, 字段2, ...)
   + 方式3 array(字段1 => 别名, 字段2, ...)
   * @param mixed $fields 数据
   * @return string
   */
  public function ParseField($fields) {
    if (is_string($fields) && strpos($fields, ',')) {
      $fields = explode(',', $fields);
    }
    $string = '';
    if (is_array($fields)) {
      $array = array();
      foreach ($fields as $key => $value) {
        if (is_numeric($key)) {
          $array[] = $this->ParseKey($value);
        } else {
          $array[] = $this->ParseKey($key).' AS '.$this->ParseKey($value);
        }
      }
      $string = implode(',', $array);
    } elseif (is_string($fields)) {
      $string = $this->ParseKey($fields);
    } else {
      $string = '*';
    }
    return ' '.$string;
  }

  /*
   * Table解析
   * @param mixed $table 数据
   * @return string
   */
  public function ParseTable($data) {
    $array = array();
    if (is_array($data)) {
      foreach ($data as $table => $alias) {
        if (is_numeric($table)) {
          $array[] = $this->ParseKey($alias);
        } else {
          $array[] = $this->ParseKey($table).' AS '.$this->ParseKey($alias);
        }
      }
    } elseif (is_string($data)) {
      $array = explode(',', $data);
      $array = array_map(array($this, 'ParseKey'), $array);
    }
    return ' '.implode(',', $array);
  }

  /*
   * Where解析
   * @param string $data 数据
   * @return string
   */
  public function ParseWhere($data) {
    if (is_string($data)) {
      $data = empty($data) ? '' : ' WHERE '.$data;
    } else {
      $data = '';
    }
    return $data;
  }

  /*
   * Limit解析
   * @param string $data 数据
   * @return string
   */
  public function ParseLimit($data) {
    $data = empty($data) ? '' : ' LIMIT '.$data;
    return $data;
  }

  /*
   * Join解析
   * @param mixed $data 数据
   * @return string
   */
  public function ParseJoin($data) {
    if (empty($data)) { return '';}
    if (is_array($data)) {
      foreach ($data as $key => $value) {
        if (strpos(strtolower($value), 'join') === false) {
          $string .= ' LEFT JOIN '.$value;
        } else {
          $string .= ' '.$value;
        }
      }
    } else {
      if (strpos(strtolower($data), 'join') === false) {
        $string = ' LEFT JOIN '.$data;
      } else {
        $string = ' '.$data;
      }
    }
    return $string;
  }

  /*
   * Order解析
   + 支持数组方式传递参数
   + 方式1 字符串方式
   + 方式2 array(排序1, 排序2, ...)
   + 方式3 array(排序字段1 => 排序方式, 排序2, ...)
   * @param mixed $data 数据
   * @return string
   */
  public function ParseOrder($data) {
    if (is_array($data)) {
      $array = array();
      foreach ($data as $field => $order) {
        if (is_numeric($field)) {
          $array[] = $this->ParseKey($order);
        } else {
          $array[] = $this->ParseKey($field).' '.$order;
        }
      }
      $data = implode(',', $array);
    }
    $data = empty($data) ? '' : ' ORDER BY '.$data;
    return $data;
  }

  /*
   * Comment解析
   * @param mixed $data 数据
   * @return string
   */
  public function ParseComment($data) {
    $data = empty($data) ? '' : '/*'.$data.'*/';
    return $data;
  }

  /*
   * 插入记录
   * @param array $data 数据
   * @param array $options 参数
   * @param boolean $replace 是否采用REPLACE语法
   * @return false | integer
   */
  public function Insert($data, $options, $replace = false) {
    $fields = $value = array();
    foreach ($data as $key => $val) {
      $val = $this->ParseValue($val);
      if (is_scalar($val)) {
        //如果是标量数据
        $fields[] = $this->ParseKey($key);
        $value[] = $val;
      }
    }
    $sql = ($replace ? 'REPLACE' : 'INSERT').' INTO '.$this->ParseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $value).')';
    $sql .= $this->ParseComment($options['comment']);
    return $this->Execute($sql);
  }

  /*
   * 更新记录
   * @param array $data 数据
   * @param array $options 参数
   * @return false | integer
   */
  public function Update($data, $options) {
    $sql = 'UPDATE '
      .$this->ParseTable($options['table'])
      .$this->ParseSet($data)
      .$this->ParseWhere($options['where'])
      .$this->ParseOrder($options['order'])
      .$this->ParseLimit($options['limit'])
      .$this->ParseComment($options['comment']);
    return $this->Execute($sql);
  }

  /*
   * 查询记录
   * @param array $options 参数
   * @return false | integer
   */
  public function Select($options) {
    $sql = 'SELECT '
      .$this->ParseField($options['field']).' FROM'
      .$this->ParseTable($options['table'])
      .$this->ParseJoin($options['join'])
      .$this->ParseWhere($options['where'])
      .$this->ParseOrder($options['order'])
      .$this->ParseLimit($options['limit'])
      .$this->ParseComment($options['comment']);
    return $this->Query($sql);
  }

  /*
   * 删除记录
   * @param array $options 参数
   * @return false | integer
   */
  public function Delete($options) {
    $sql = 'DELETE FROM '
      .$this->ParseTable($options['table'])
      .$this->ParseWhere($options['where'])
      .$this->ParseOrder($options['order'])
      .$this->ParseLimit($options['limit'])
      .$this->ParseComment($options['comment']);
    return $this->Execute($sql);
  }

  /*
   * 获取最近一次查询的SQL语句
   * @return string
   */
  public function GetLastSql() {
    return $this->QueryStr;
  }

  /*
   * 获取最近一次插入的ID
   * @return string
   */
  public function GetLastId() {
    return $this->LastInsertId;
  }

  /*
   * 获取最近一次错误信息
   * @return string
   */
  public function GetError() {
    return $this->Error;
  }

  /*
   * 关闭数据库
   + 具体关闭方式由数据库驱动类自行定义
   */
  public function Close() {}

  /*
   * 析构方法
   * @return string
   */
  public function __destruct() {
    if ($this->CurQueryId) {
      $this->Free();
    }
    $this->Close();
  }
}